import sys

from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel, QCheckBox, QPushButton, QGroupBox, QLayout
from PySide6.QtCore import Qt, Signal, QThread, Slot

from openpyxl import load_workbook, Workbook
from pathlib import Path

from api import api_convert, api_read, api_ui

class LedgerConvertUi(QGroupBox):
    rel_tip_signal = Signal(str, str)
    show_msg_signal = Signal(dict)
    send_to_subthread_signal = Signal(bool)
    request_ledger_signal = Signal()
    received_ledger_signal = Signal()
    log_signal = Signal(str, str)

    def __init__(self, group_box_style: str):
        super().__init__()
        self.setStyleSheet(group_box_style)
        self.setTitle('文件转换')

        self.all_checkbox = []          # 存储所有的checkbox
        self.resize(700, 480)
        self.setup_ui()

    def setup_ui(self):
        # 提示区域
        # 借款报告明细表提示
        self.loan_tip_widget = QWidget()
        self.loan_tip_widget.setStyleSheet('background-color: #F5F5DC;')
        tip_layout = QVBoxLayout(self.loan_tip_widget)
        tip_layout.addWidget(QLabel('执行前：'))
        first_ckbox = QCheckBox('修改明细表中往来单位')
        tip_layout.addWidget(first_ckbox)
        # 项目资金计划提示
        self.our_plan_tip_widget = QWidget()
        self.our_plan_tip_widget.hide()
        self.our_plan_tip_widget.setStyleSheet('background-color: #F5F5DC;')
        tip_layout = QVBoxLayout(self.our_plan_tip_widget)
        tip_layout.addWidget(QLabel('程序生成项目资金计划文件，无需选择'))


        # 设置单位，元/万元
        unit_layout, box_list = api_ui.checkbox_ui('单位', ('元', '万元'))
        (self.yuan_checkbox, self.wanyuan_checkbox) = box_list
        self.all_checkbox.append(box_list)
        self.yuan_checkbox.stateChanged.connect(self.checkbox_clicked)
        self.wanyuan_checkbox.stateChanged.connect(self.checkbox_clicked)
        # 暂时用不到单位切换，禁用
        self.yuan_checkbox.setEnabled(False)
        self.wanyuan_checkbox.setEnabled(False)

        # 设置转换文件
        convert_file_layout, box_list = api_ui.checkbox_ui('转换文件', ('借款报告明细表', '公司资金计划', '项目资金计划'))
        (self.loan_checkbox, self.plan_checkbox, self.our_plan_checkbox) = box_list
        self.all_checkbox.append(box_list)
        self.loan_checkbox.stateChanged.connect(self.convert_file_checkbox_clicked)
        self.loan_checkbox.stateChanged.connect(self.checkbox_clicked)
        self.plan_checkbox.stateChanged.connect(self.convert_file_checkbox_clicked)
        self.plan_checkbox.stateChanged.connect(self.checkbox_clicked)
        self.our_plan_checkbox.stateChanged.connect(self.convert_file_checkbox_clicked)
        self.our_plan_checkbox.stateChanged.connect(self.checkbox_clicked)

        
        # 选择转换文件的路径
        to_file_layout, self.to_file_label, self.to_file_edit, self.to_file_button = api_ui.file_path_ui('借款报告明细表')

        # 开始按钮
        self.convert_start_btn = QPushButton('开始')
        self.convert_start_btn.setFixedWidth(60)
        self.convert_start_btn.clicked.connect(self.check_input)

        # 宏观布局
        main_layout = QVBoxLayout(self)
        main_layout.setAlignment(Qt.AlignCenter)
        main_layout.setSizeConstraint(QLayout.SetNoConstraint)
        main_layout.addWidget(self.loan_tip_widget)
        main_layout.addWidget(self.our_plan_tip_widget)
        main_layout.addLayout(unit_layout)
        main_layout.addLayout(convert_file_layout)
        main_layout.addLayout(to_file_layout)
        main_layout.addWidget(self.convert_start_btn, alignment=Qt.AlignCenter)

    def run_thread(self):
        self.convert_start_btn.setEnabled(False)
        self.convert_start_btn.setText('执行中')
        self.rel_tip_signal.emit('开始', 'blue')

        unit = 'yuan' if self.yuan_checkbox.isChecked() else 'wanyuan'
        if self.loan_checkbox.isChecked():
            file_type = '借款报告明细表'
        elif self.plan_checkbox.isChecked():
            file_type = '公司资金计划'
        else:
            file_type = '项目资金计划'
        to_file_path = Path(self.to_file_edit.text())
        self.thread = Ledger2othersThread(unit, file_type, self.ledger_path, to_file_path)
        self.thread.rel_tip_signal.connect(self.rel_tip_signal)
        self.thread.finished_signal.connect(self.after_finish)
        self.thread.log_signal.connect(lambda x: self.log_signal.emit(__name__, x))
        self.send_to_subthread_signal.connect(self.thread.receive_decision)
        self.thread.start()

    @Slot(bool, list)
    def checkbox_clicked(self):
        # 通过 self.sender() 获取发送信号的对象，再通过 self.all_checkbox 找到其他关联的 checkbox
        current_checkbox = self.sender()
        for box_list in self.all_checkbox:
            if current_checkbox in box_list:
                current_boxlist = box_list
        length = len(current_boxlist)
        state = current_checkbox.isChecked()
        if state:
            for box in current_boxlist:
                if box == current_checkbox:
                    continue
                box.setChecked(False)
        else:
            if length == 2:
                # exclusive_checkbox = box_list[abs(box_list.index(current_checkbox) - 1)]        # 两种写法
                exclusive_checkbox, = [x for x in current_boxlist if x != current_checkbox]
                exclusive_checkbox.setChecked(True)
        if self.loan_checkbox.isChecked():
            self.loan_tip_widget.show()
            self.our_plan_tip_widget.hide()
            self.to_file_button.setEnabled(True)
            self.to_file_edit.setObjectName('借款报告明细表')       # 让选择文件路径的编辑框知道是哪个文件，按钮点击后可以根据文件类型选择文件
        elif self.plan_checkbox.isChecked():
            self.loan_tip_widget.hide()
            self.our_plan_tip_widget.hide()
            self.to_file_button.setEnabled(True)
            self.to_file_edit.setObjectName('公司资金计划')
        else:
            self.loan_tip_widget.hide()
            self.our_plan_tip_widget.show()
            self.to_file_button.setEnabled(False)
            self.to_file_edit.setObjectName('项目资金计划')
    
    @Slot(bool)
    def convert_file_checkbox_clicked(self):
        current_box = self.sender()
        if current_box.isChecked():
            file_type = current_box.text()
            self.to_file_label.setText(file_type)
            self.to_file_edit.setToolTip(f'请选择{file_type}')

    @Slot()
    def check_input(self):
        self.request_ledger_signal.emit()
        if self.ledger_path == None:
            self.rel_tip_signal.emit('台账路径为空，结束运行', 'red')
            return
        if self.yuan_checkbox.isChecked() or self.wanyuan_checkbox.isChecked():
            if self.loan_checkbox.isChecked() or self.plan_checkbox.isChecked() or self.our_plan_checkbox.isChecked():
                if self.to_file_edit.text() or self.our_plan_checkbox.isChecked():    # 项目资金计划自动生成文件，无需选择
                    self.run_thread()
                else:
                    self.show_msg_signal.emit({'message': '请选择转换文件路径'})
            else:
                self.show_msg_signal.emit({'message': '请选择转换文件类型'})
        else:
            self.show_msg_signal.emit({'message': '请选择单位'})

    @Slot(int)
    def after_finish(self, sign: int):
        self.convert_start_btn.setText('开始')
        self.convert_start_btn.setEnabled(True)
        if sign:
            self.rel_tip_signal.emit('完成', 'green')
            self.rel_tip_signal.emit('-'*99, 'green')
        else:
            self.rel_tip_signal.emit('提前结束', 'red')

    @Slot(bool)
    def receive_decision(self, choice):
        """槽函数，用于接收用户选择，并发送给子线程"""
        self.send_to_subthread_signal.emit(choice)
    
    @Slot(Path)
    def receive_ledger_path(self, ledger_path: list):
        self.ledger_path = ledger_path
        self.received_ledger_signal.emit()
        
class Ledger2othersThread(QThread):
    rel_tip_signal = Signal(str, str)
    finished_signal = Signal(int)
    log_signal = Signal(str)

    def __init__(self, unit: str, file_type: str, ledger_path: list, to_file_path: Path):
        super().__init__()
        self.unit = unit        # 暂时用不到
        self.file_type = file_type
        self.ledger_path = ledger_path
        self.to_file_path = to_file_path

        self.plan_files = None
        self.ledger_content = None
    
    @Slot(str, object)
    def get_value(self, key, value):
        setattr(self, key, value)

    @Slot(bool)
    def receive_decision(self, choice):
        """槽函数，用于接收用户选择"""
        self.user_choice = choice

    def run(self):
        get_files = api_read.GetFiles(self.ledger_path, '结算支付台账')
        get_files.value_signal.connect(lambda files: self.get_value('ledger_files', files))      # 定义 self.ledger_files
        get_files.rel_tip_signal.connect(self.rel_tip_signal)
        get_files.finished_signal.connect(lambda x: self.get_value('finished_sign', x))      # 定义 self.finished_sign
        get_files.run()

        if self.finished_sign == 0:
            self.finished_signal.emit(0)
            return          # 如果函数提前结束，则结束线程
        
        if self.file_type == '借款报告明细表':
            header_info = {'start_row': 4, 'end_row': 4}
            need_change_columns = {'款项性质': 'B', '结算金额（含税）': 'D', '应付结算款': 'F', '实际已付款': 'G', '剩余应付款': 'H', '本期借款': 'I', '备注': 'J'}
            manual_change_columns = ['本期借款', '备注']

            self.wb = load_workbook(self.to_file_path)            # 打开借款报告明细表
            sheet_names = self.wb.sheetnames
            for sheet_name in sheet_names:
                self.fill_in(self.to_file_path, sheet_name, sheet_name, header_info, need_change_columns, manual_change_columns)
        elif self.file_type == '公司资金计划':
            header_info = {'start_row': 3, 'end_row': 5}
            # 需要清空的列，key：结算支付台账表头，value：公司资金计划所在列
            need_change_columns = {'支付方式': 'B', '款项性质': 'C', '队伍名称': 'D', '结算金额（不含税）': 'F', '结算税金': 'G', '结算金额（含税）': 'H', 
                                   '质量保证金': 'I', '民工工资保证金': 'J', '已付结算款': 'M', '账面应付账款余额': 'T', '工行一年期': 'N', '农行一年期': 'O', 
                                   '建行一年期': 'P', '本月预计结算': 'AI', '备注': 'AJ'}
            manual_change_columns = ['支付方式', '本月预计结算', '备注']

            get_files = api_read.GetFiles(self.to_file_path, '资金计划')      # 这里不是线程
            get_files.value_signal.connect(lambda value: self.get_value('plan_files', value))      # 定义 self.plan_files
            get_files.rel_tip_signal.connect(self.rel_tip_signal)
            get_files.finished_signal.connect(lambda x: self.get_value('finished_sign', x))        # 定义 self.finished_sign
            get_files.run()

            if self.finished_sign == 0:
                self.finished_signal.emit(0)
                return            # 如果函数提前结束，则结束线程
            for progromme_name, plan_file in self.plan_files.items():
                self.wb = load_workbook(plan_file)
                added_clear_columns = []
                self.fill_in(plan_file, progromme_name, '项目明细表', header_info, need_change_columns, manual_change_columns, result_in_yuan=False)
        else:           # 项目资金计划
            headers = ['序号', '队伍名称', '往来单位名称', '结算金额（含税）', '应付结算款', '实际已付款', '剩余应付款', '部门', '款项性质']
            wb = Workbook()
            for progromme_name, ledger_file in self.ledger_files.items():
                self.rel_tip_signal.emit(progromme_name, 'black')
                ledger_content = api_read.ReadLedger(ledger_file)       # 从结算支付台账中提取数据
                ledger_content.rel_tip_signal.connect(self.rel_tip_signal)
                ledger_content.value_signal.connect(lambda value: self.get_value('ledger_content', value))        # 定义 self.ledger_content
                ledger_content.finished_signal.connect(lambda x: self.get_value('finished_sign', x))
                ledger_content.log_signal.connect(self.log_signal)
                ledger_content.run()
                if self.finished_sign == 0:
                    self.finished_signal.emit(0)
                    return
                for unit_name, unit_content in self.ledger_content.copy().items():
                    remain_payment = unit_content['剩余应付款']
                    if eval(remain_payment) < 1:        # 删除实际为0，但读取为极小数（如；-1.4551915228366852e-11）的情况
                        del self.ledger_content[unit_name]
                self.ledger_content = dict(sorted(self.ledger_content.items(), key=lambda x: (x[1]['部门'], x[1]['款项性质'])))       # 按部门名称排序
                
                wb.create_sheet(progromme_name)
                ws = wb[progromme_name]
                ws.append(headers)
                num = 1
                current_row = 2
                for unit_content in self.ledger_content.values():
                    ws.cell(current_row, 1).value = num
                    for col_name in headers[1:]:
                        try:
                            float(unit_content[col_name])       # 判断是否为数字
                        except ValueError:
                            value_to_wan = unit_content[col_name]
                        else:
                            value_to_wan = api_convert.convert_to_wan(unit_content[col_name])
                        ws.cell(current_row, headers.index(col_name)+1).value = value_to_wan
                    num += 1
                    current_row += 1
            file_path = f'{self.ledger_path}/our_plan.xlsx'
            wb.save(file_path)
            self.rel_tip_signal.emit(f'生成文件已保存至：{file_path}', 'green')

        self.finished_signal.emit(1)

    def fill_in(self, file_path: Path, programme_name: str, sheet_name: str, header_info: dict, need_change_columns: dict, manual_change_columns: list,
                result_in_yuan: bool = True, added_clear_columns: list = []):
        file_type = file_path.stem.split('-')[1]
        try:
            ledger_file = self.ledger_files[programme_name]
        except KeyError:
            self.rel_tip_signal.emit(f'{programme_name} 对应的台账文件不存在，跳过', 'orange')
            return
        
        read_ledger = api_read.ReadLedger(ledger_file)
        read_ledger.rel_tip_signal.connect(self.rel_tip_signal)
        read_ledger.value_signal.connect(lambda value: self.get_value('ledger_content', value))
        read_ledger.finished_signal.connect(lambda x: self.get_value('finished_sign', x))
        read_ledger.log_signal.connect(self.log_signal)
        read_ledger.run()
        if self.finished_sign == 0:
            self.finished_signal.emit(0)
            return

        ws = self.wb[sheet_name]
        header_start_row = header_info['start_row']
        headers_info = [(cell.column, cell.value) for cell in ws[header_start_row]]
        for the_cell in ws['A']:        # 探索主要内容有多少行。必须有“合计”行
            if the_cell.value == '合计':
                last_content_row = the_cell.row - 1
                break
        # 删除填充列的内容
        for col_letter in need_change_columns.values():
            header_end_row = header_info['end_row']
            for row_index in range(header_end_row+1, last_content_row + 1):
                ws[f'{col_letter}{row_index}'].value = None
        # 开始填充值
        auto_change_columns = need_change_columns.copy()        # 复制一下，否则会改变全局变量
        for col in manual_change_columns:
            auto_change_columns.pop(col)        # 剔除需要手动填充的列
        header_end_row = header_info['end_row']
        for row_index in range(header_end_row+1, last_content_row + 1):
            for col_name, col_letter in auto_change_columns.items():            # col_name 为结算支付台账的列名，col_letter 为公司资金计划的列字母
                # 找到单位的数据在台账中的位置
                unit_col = [t[0] for t in headers_info if t[1] == '往来单位名称'][0]
                the_cell = ws.cell(row=row_index, column=unit_col)
                unit_name = the_cell.value
                if unit_name == None:
                    self.rel_tip_signal.emit(f'{sheet_name} {file_type} 中往来单位单元格（{the_cell.coordinate}）为空，跳过', 'orange')
                    break
                elif unit_name not in self.ledger_content:
                    self.rel_tip_signal.emit(f'{sheet_name} {file_type} 中的 {unit_name} 未在结算支付台账中找到，请检查！', 'orange')
                    break
                the_value = self.ledger_content[unit_name][col_name]
                try:
                    the_value = round(float(the_value), 2)
                except ValueError:          # 单元格内容为文本
                    pass
                else:
                    if result_in_yuan == False:       # 金额单位为元，需转换为万元
                        the_value = api_convert.convert_to_wan(the_value)
                ws[f'{col_letter}{row_index}'].value = the_value
            else:
                self.ledger_content.pop(unit_name)
        self.wb.save(file_path)       # 每个工作表填充完毕后，保存一下
        self.rel_tip_signal.emit(f'{programme_name} {file_type} 已处理完毕', 'green')


